package main

import (
	"net/http"
	"strconv"

	"github.com/labstack/echo/v4"
	"gorm.io/gorm"
)

// Model
type Post struct {
	BaseModel
	Title    string `json:"title"   validate:"required"`
	Content  string `json:"content" validate:"required"`
	UserID   uint   `json:"user_id" validate:"required"`
	Comments []Comment
}

type PostDisplay struct {
	Post
	Author UserBrief `json:"author"`
}

func (post *Post) fetch(db *gorm.DB, id int) error {
	result := db.Preload("Comments").First(&post, id)
	return result.Error
}

func (post *Post) fetchWithoutComments(db *gorm.DB, id int) error {
	result := db.First(&post, id)
	return result.Error
}

func (post *Post) checkPermition(c *ExtendedContext) error {
	user, err := getUser(c)
	if err != nil {
		return err
	}
	if !user.IsAdmin && user.ID != post.UserID {
		return echo.NewHTTPError(http.StatusUnauthorized, "没有权限")
	}
	return nil
}

func (post *Post) toDisplay(db *gorm.DB) *PostDisplay {
	var user User
	db.First(&user, post.UserID)
	return &PostDisplay{*post, UserBrief{user.ID, user.Username, user.Email}}
}

// Index
func (post *Post) Index(c *ExtendedContext) error {
	var posts []Post
	c.DB().Find(&posts)
	posts_display := make([]*PostDisplay, 0)
	for _, post := range posts {
		posts_display = append(posts_display, post.toDisplay(c.DB()))
	}
	return c.JSON(http.StatusOK, posts_display)
}

// Show
func (post *Post) Show(c *ExtendedContext) error {
	id, _ := strconv.Atoi(c.Param("id"))
	if err := post.fetch(c.DB(), id); err != nil {
		return echo.NewHTTPError(http.StatusNotFound, "Post 不存在")
	}
	return c.JSON(http.StatusOK, post.toDisplay(c.DB()))
}

// Create
func (post *Post) Create(c *ExtendedContext) error {
	if err := c.Bind(post); err != nil {
		return echo.NewHTTPError(http.StatusBadRequest, err.Error())
	}
	if err := c.Validate(post); err != nil {
		return echo.NewHTTPError(http.StatusBadRequest, err.Error())
	}
	if err := post.checkPermition(c); err != nil {
		return err
	}
	if result := c.DB().Create(post); result.Error != nil {
		return echo.NewHTTPError(http.StatusBadRequest, result.Error.Error())
	}
	return c.NoContent(http.StatusCreated)
}

// Update
func (post *Post) Update(c *ExtendedContext) error {
	id, _ := strconv.Atoi(c.Param("id"))
	post.fetchWithoutComments(c.DB(), id)
	if err := post.checkPermition(c); err != nil {
		return err
	}

	if err := c.Bind(post); err != nil {
		return echo.NewHTTPError(http.StatusBadRequest, err.Error())
	}
	post.UserID = 9999 // userid 不用更新，但为了通过验证，必须提供一个非零值
	if err := c.Validate(post); err != nil {
		return echo.NewHTTPError(http.StatusBadRequest, err.Error())
	}

	if result := c.DB().Model(post).Updates(map[string]interface{}{"Title": post.Title, "Content": post.Content}); result.Error != nil {
		return echo.NewHTTPError(http.StatusBadRequest, result.Error.Error())
	}
	return nil
}

// Delete
func (post *Post) Delete(c *ExtendedContext) error {
	id, _ := strconv.Atoi(c.Param("id"))
	post.fetchWithoutComments(c.DB(), id)
	if err := post.checkPermition(c); err != nil {
		return err
	}

	if result := c.DB().Delete(post, id); result.Error != nil {
		return echo.NewHTTPError(http.StatusBadRequest, result.Error.Error())
	}
	return c.NoContent(http.StatusOK)
}

func PostRegister(e *echo.Echo) {
	h := MethodWrapper(Post{})
	g := e.Group("/api/posts")
	g.GET("/", h("Index"))
	g.GET("/:id/", h("Show"))
	g.POST("/", h("Create"), NeedAuth)
	g.PUT("/:id/", h("Update"), NeedAuth)
	g.DELETE("/:id/", h("Delete"), NeedAuth)
}
